// ------------------------------------------------------------------------------------------
// Licensed by Interprise Solutions.
// http://www.InterpriseSolutions.com
// For details on this license please visit  the product homepage at the URL above.
// THE ABOVE NOTICE MUST REMAIN INTACT.
// ------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Security;
using System.Security;
using System.Security.Principal;

namespace InterpriseSuiteEcommerceCommon.InterpriseIntegration.Authentication
{
    public static class RestrictedResourceManager
    {
        #region Variable Declaration

        private static Dictionary<string, RestrictedResourceInfo> _resourceInfoCacheByResourcePath;
        private static object _restrictionLock;

        #endregion

        #region RestrictedResourceManager
        /// <summary>
        /// Type constructor
        /// </summary>
        static RestrictedResourceManager()
        {
            _resourceInfoCacheByResourcePath = new Dictionary<string, RestrictedResourceInfo>();
            _restrictionLock = new object();
        }
        #endregion

        #region IsEnabled
        /// <summary>
        /// Gets whether the restricted configuration is enabled
        /// </summary>
        /// <returns></returns>
        public static bool IsEnabled()
        {
            return RestrictedResourceConfiguration.Instance.IsEnabled;
        }
        #endregion

        #region CreateAuthenticationTicket
        /// <summary>
        /// Creates the authentication ticket
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="cookieName"></param>
        public static void CreateAuthenticationTicket(string userName, string cookieName)
        {
            CreateAuthenticationTicket(userName, cookieName, false);
        }
        #endregion

        #region CreateAuthenticationTicket
        /// <summary>
        /// Creates the authentication ticket
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="cookieName"></param>
        /// <param name="doRedirect"></param>
        public static void CreateAuthenticationTicket(string userName, string cookieName, bool doRedirect)
        {
            RestrictedResourceInfo resourceInfo = FindResourceInfoByName(cookieName);
            if (null == resourceInfo)
            {
                throw new ArgumentException("Restriction not configured!!!", cookieName);
            }

            FormsAuthenticationTicket ticket =
            new FormsAuthenticationTicket(1,
                userName,
                DateTime.Now,
                DateTime.Now.AddMinutes(resourceInfo.TimeOut),
                false,
                string.Empty
            );

            // encrypt the cookie
            string encryptedTicket = FormsAuthentication.Encrypt(ticket);

            HttpCookie cookie = new HttpCookie(
                cookieName, encryptedTicket
            );

            cookie.HttpOnly = true;
            cookie.Path = FormsAuthentication.FormsCookiePath;
            cookie.Secure = FormsAuthentication.RequireSSL;
            if (string.IsNullOrEmpty(FormsAuthentication.CookieDomain))
            {
                cookie.Domain = FormsAuthentication.CookieDomain;
            }
            cookie.Expires = ticket.Expiration;

            HttpContext ctx = HttpContext.Current;
            ctx.Response.Cookies.Add(cookie);

            if (doRedirect)
            {
                ctx.Response.Redirect(resourceInfo.DefaultUrl);
            }
        }
        #endregion

        #region SignOut
        /// <summary>
        /// Signout Authentication
        /// </summary>
        /// <param name="cookieName"></param>
        /// <param name="doRedirect"></param>
        public static void SignOut(string cookieName, bool doRedirect)
        {
            RestrictedResourceInfo resourceInfo = FindResourceInfoByName(cookieName);
            if (null == resourceInfo)
            {
                throw new ArgumentException("Restriction not configured!!!", cookieName);
            }

            HttpContext ctx = HttpContext.Current;
            string dummyCookieValue = string.Empty;

            if (ctx.Request.Browser["supportsEmptyStringInCookieValue"] == "false")
            {
                dummyCookieValue = "NoCookie";
            }

            HttpCookie cookie = new HttpCookie(cookieName, dummyCookieValue);
            cookie.HttpOnly = true;
            cookie.Path = FormsAuthentication.FormsCookiePath;
            cookie.Expires = new DateTime(0x7cf, 10, 12);
            cookie.Secure = FormsAuthentication.RequireSSL;
            if (string.IsNullOrEmpty(FormsAuthentication.CookieDomain))
            {
                cookie.Domain = FormsAuthentication.CookieDomain;
            }
            ctx.Response.Cookies.Remove(cookieName);
            ctx.Response.Cookies.Add(cookie);

            if (doRedirect)
            {
                ctx.Response.Redirect(resourceInfo.LoginUrl);
            }
        }
        #endregion

        #region FindResourceInfoByName
        /// <summary>
        /// Gets the resource info
        /// </summary>
        /// <param name="cookieName"></param>
        /// <returns></returns>
        private static RestrictedResourceInfo FindResourceInfoByName(string cookieName)
        {
            // validate the resource name against some patterns
            foreach (RestrictedResourceInfo resourceInfo in RestrictedResourceConfiguration.Instance.ProtectedResources)
            {
                // The comparison will be case-sensitive                
                if (resourceInfo.Name.Equals(cookieName))
                {
                    return resourceInfo;
                }
            }

            // resource not found
            return null;
        }
        #endregion

        #region IsRestricted
        /// <summary>
        /// Gets whether the resource is restricted
        /// </summary>
        /// <param name="resource"></param>
        /// <returns></returns>
        public static bool IsRestricted(string resource)
        {
            RestrictedResourceInfo resourceInfo = null;
            return IsRestricted(resource, out resourceInfo) && null != resourceInfo;
        }
        #endregion

        #region IsRestricted
        /// <summary>
        /// Gets whether the resource is restricted
        /// </summary>
        /// <param name="resource"></param>
        /// <param name="resourceInfo"></param>
        /// <returns></returns>
        internal static bool IsRestricted(string resource, out RestrictedResourceInfo resourceInfo)
        {
            lock (_restrictionLock)
            {
                // let's make sure we handle all cases!!!
                resource = resource.ToUpperInvariant();

                // check if we have already the resource in the cache
                // meaning the resource is configured to be restricted
                if (_resourceInfoCacheByResourcePath.ContainsKey(resource))
                {
                    //  Either we found the resource info
                    //  in the cache from a previous lookup
                    //  or we didn't so this would return null.
                    resourceInfo = _resourceInfoCacheByResourcePath[resource];

                    return null != resourceInfo;
                }

                // if we reached this point, this may be the first time
                // we're going to get the restriction info for this resource

                // validate the resource name against some patterns
                foreach (RestrictedResourceInfo restrictedResourceInfo in RestrictedResourceConfiguration.Instance.ProtectedResources)
                {
                    if (restrictedResourceInfo.Qualifier.Qualifies(resource))
                    {
                        // we've found the resource info
                        resourceInfo = restrictedResourceInfo;

                        // cache the resource for later use
                        _resourceInfoCacheByResourcePath.Add(resource, resourceInfo);

                        // leave
                        return true;
                    }
                }

                // at this point
                // we didn't find any resource for it
                // Cache the resource for later lookup
                _resourceInfoCacheByResourcePath.Add(resource, null);
                resourceInfo = null;

                return false;
            }
        }
        #endregion
    }
}
